#include "textprocessor.h"

#include <regex>
#include <cassert>

using namespace std;

TextProcessor::TextProcessor()
{

}

std::shared_ptr<std::vector<std::string> > TextProcessor::extractHeader(std::string &source)
{
    shared_ptr<vector<string>> result(new vector<string>);

    //string includePattern = "# *include *\\\"[\\\\/\\._a-zA-Z0-9]+\\\"";
    string includePattern = "# *include *\\<[\\\\/\\._a-zA-Z0-9]+\\>";
    regex includeRe(includePattern);

    //string headerPattern = "\\\"[\\\\/\\._a-zA-Z0-9]+\\\"";
    string headerPattern = "\\<[\\\\/\\._a-zA-Z0-9]+\\>";
    regex headerRe(headerPattern);

    string matchStr;
    smatch match;

    string header;

    for (sregex_iterator it(source.begin(), source.end(), includeRe), end_it; it != end_it; it++)
    {
        matchStr = it->str();
        if (regex_search(matchStr, match, headerRe))
        {
            header = match.str();
            header = header.substr(1, header.length() - 2);
            result->push_back(header);
        }
    }

    return result;
}

std::shared_ptr<std::string> TextProcessor::filterComment(std::string &source)
{
    enum E_STATE
    {
        S0,
        S1,
        S2,
        S3,
        S4,
        S5,
        S6
    };

    enum E_ACTION
    {
        EAT,
        OUT,
        STORE,
        FLUSH,
        CLEAR
    };

    shared_ptr<string> result = make_shared<string>("");

    E_STATE state = S0;
    E_ACTION action = EAT;
    char store = '\0';
    for (const char c : source)
    {
        switch (state)
        {
        case S0:
        {
            if (c == '/')
            {
                action = STORE;
                state = S1;
            }
            else if (c == '"')
            {
                action = OUT;
                state = S2;
            }
            else
            {
                action = OUT;
                state = S0;
            }
            break;
        }
        case S1:
        {
            if (c == '*')
            {
                action = CLEAR;
                state = S4;
            }
            else if (c == '/')
            {
                action = CLEAR;
                state = S3;
            }
            else if (c == '"')
            {
                action = FLUSH;
                state = S2;
            }
            else
            {
                action = FLUSH;
                state = S0;
            }
            break;
        }
        case S2:
        {
            if (c == '"')
            {
                action = OUT;
                state = S0;
            }
            else
            {
                action = OUT;
                state = S2;
            }
            break;
        }
        case S3:
        {
            if (c == '\n')
            {
                action = EAT;
                state = S0;
            }
            else if (c == '\r')
            {
                action = EAT;
                state = S6;
            }
            else
            {
                action = EAT;
                state = S3;
            }
            break;
        }
        case S4:
        {
            if (c == '*')
            {
                action = STORE;
                state = S5;
            }
            else
            {
                action = EAT;
                state = S4;
            }
            break;
        }
        case S5:
        {
            if (c == '/')
            {
                action = CLEAR;
                state = S0;
            }
            else
            {
                action = CLEAR;
                state = S4;
            }
            break;
        }
        case S6:
        {
            if (c == '\n')
            {
                action = CLEAR;
                state = S0;
            }
            else
            {
                action = OUT;
                state = S0;
            }
            break;
        }
        default:
            assert(false);
        }
        switch (action)
        {
        case EAT:
        {
            break;
        }
        case OUT:
        {
            result->append(1, c);
            break;
        }
        case STORE:
        {
            store = c;
            break;
        }
        case FLUSH:
        {
            result->append(1, store);
            result->append(1, c);
            break;
        }
        case CLEAR:
        {
            break;
        }
        default:
            assert(false);
        }
    }
    return result;
}
